#include "../include/ppcustomermovie.h"
#include "../include/ppread.h"
#include <list>
#include <iostream>
#include <cstdlib>
#include <cmath>
using namespace std;
void ppcustomermovie::populate(ppread moviecustomerrating){
    vector<int> vecCustID = moviecustomerrating.getCustIDs();
    int movieID = moviecustomerrating.getMovieID();
    for(unsigned int j=0; j < vecCustID.size(); j++){
        map<int, map<short, short> >::iterator itrCMR = mpCustomerMovieRating.find(vecCustID[j]);
        if(itrCMR != mpCustomerMovieRating.end()){
            //If we already have a record for the customer then just insert the pair.
            (*itrCMR).second.insert(pair<short, short>(movieID, moviecustomerrating.getRating(vecCustID[j])));
        }else{
            //For the first tiem we add the new map there.
            map<short, short> mpmovierating;
            mpmovierating[movieID] = moviecustomerrating.getRating(vecCustID[j]);
            mpCustomerMovieRating[vecCustID[j]] = mpmovierating;
        }
    }
}

void ppcustomermovie::calculateSD(){
    //for every Customer
    //calculate Mean.
        //Sum up all the ratings for one customer and then devide by count of sums.
    //calculate SD.
        //Sum the squared difference between rating for this movie and mean of the ratings for this user.
        //Devide the sum by number of movies rated.
    for(map<int, map<short, short> >::iterator itrCMR = mpCustomerMovieRating.begin();
    itrCMR != mpCustomerMovieRating.end();
    itrCMR++){
        long int nSum = 0; float fAvg;
        for(map<short, short>::iterator itrMR  = (*itrCMR).second.begin();
        itrMR != (*itrCMR).second.end();
        itrMR++){
            nSum += (*itrMR).second;
        }

        fAvg = (float) nSum / (float) (*itrCMR).second.size();
        mpMean[(*itrCMR).first] = fAvg;
    }

    for(map<int, map<short, short> >::iterator itrCMR = mpCustomerMovieRating.begin();
    itrCMR != mpCustomerMovieRating.end();
    itrCMR++){
        long int nSum = 0; float fSD;
        for(map<short, short>::iterator itrMR  = (*itrCMR).second.begin();
        itrMR != (*itrCMR).second.end();
        itrMR++){
            int nDiff = ((*itrMR).second - mpMean[(*itrCMR).first]);
            nSum += (nDiff*nDiff);
        }

        fSD = (float) nSum / (float) (*itrCMR).second.size();
        mpCustSD[(*itrCMR).first] = fSD;
    }

}

map<short, char> ppcustomermovie::getMovieIDs(int custID){
    map<short, char> mpMovieIDs;
    map<short, short> mpmovierating = mpCustomerMovieRating[custID];
    for(map<short, short>::iterator itrmp = mpmovierating.begin();
        itrmp != mpmovierating.end();
        itrmp++){
            mpMovieIDs[itrmp->first] = 1;
        }
    return mpMovieIDs;
}

bool ppcustomermovie::findInteresting(int nCustID, short nMovieID){
    map<short, char> mpMvIdsCust = getMovieIDs(nCustID);

    if(mpMvIdsCust.find(nMovieID) == mpMvIdsCust.end()){
        return false;
    }else{
        return true;
    }
}

vector<int> ppcustomermovie::getCustWhoSawMovie(short MovieID){

    vector<int> vecCustWhoSawMovie;

    for(map<int, map<short, short> >::iterator itrCMR = mpCustomerMovieRating.begin();
        itrCMR != mpCustomerMovieRating.end();
        itrCMR++){
        if(findInteresting((*itrCMR).first, MovieID)){
            vecCustWhoSawMovie.push_back((*itrCMR).first);
        }else{
            continue;
        }
    }

    return vecCustWhoSawMovie;
}

double ppcustomermovie::findDistanceInteresting(int custID1, int custID2, short movieID){

    map<short, char> mpMvIdsCust1 = getMovieIDs(custID1);
    map<short, char> mpMvIdsCust2 = getMovieIDs(custID2);

    if(mpMvIdsCust2.find(movieID) == mpMvIdsCust2.end()){
        return -1;
    }else{
        return findDistance(custID1, custID2);
    }

}

double ppcustomermovie::findDistance(int custID1, int custID2){
    //Check the commons.
    //from the commons find the smallest and then find
    //Sum of distances between the common movies.
    //Divide the sum by #common movies that our distance.

    map<short, char> mpMvIdsCust1 = getMovieIDs(custID1);
    map<short, char> mpMvIdsCust2 = getMovieIDs(custID2);

    double common =0.0, sumofdiff=0.0;
    map<short, char>::iterator itrMvIdsCust;
    map<short, char>::iterator itrMvIdsCustfind;

    for(itrMvIdsCust = mpMvIdsCust1.begin();
        itrMvIdsCust != mpMvIdsCust1.end();
        itrMvIdsCust++){
            itrMvIdsCustfind = mpMvIdsCust2.find((*itrMvIdsCust).first);
            //common = 0.0;
            if(itrMvIdsCustfind != mpMvIdsCust2.end()){
                common++;
                sumofdiff += fabs((((*(mpCustomerMovieRating.find(custID1))).second[(*itrMvIdsCustfind).first] - mpMean[custID1])/mpCustSD[custID1])\
                    - (((*(mpCustomerMovieRating.find(custID2))).second[(*itrMvIdsCustfind).first] - mpMean[custID2])/mpCustSD[custID2]));
            }
        }

    if(common != 0)
        return sumofdiff / common;
    else
        return -1.0;

}

vector<int> ppcustomermovie::getCustIDs(){
    vector<int> vecCustID;

    for(map<int, map<short, short> >::iterator itrCustMovieRating = mpCustomerMovieRating.begin();
        itrCustMovieRating !=  mpCustomerMovieRating.end();
        itrCustMovieRating++){
        vecCustID.push_back((*itrCustMovieRating).first);
    }

    return vecCustID;
}

vector<int> ppcustomermovie::findNearestK(map<int, double> mpCustDist, int K){
    vector<int> nearestK;
    list<Nlist> lstCustDist;
    int nCnt = 0;
    for(map<int, double>::iterator itrCustDist = mpCustDist.begin();
        itrCustDist != mpCustDist.end();
        itrCustDist++){
        lstCustDist.push_back(Nlist((*itrCustDist).first, (*itrCustDist).second));
    }

    lstCustDist.sort();
    for(list<Nlist>::iterator itrlstCustDist = lstCustDist.begin();
        itrlstCustDist != lstCustDist.end() && nCnt < K;
        itrlstCustDist++){
            nearestK.push_back((*itrlstCustDist).custID);
            nCnt++;
    }

    return nearestK;
}

short ppcustomermovie::getRating(int CustID, short movieID){
    //cout<<"Cust ID is "<<CustID<<" Movie ID"<<movieID<<endl;
    //cout<<"Rating is "<<(*(*(mpCustomerMovieRating.find(CustID))).second.find(movieID)).second<<endl;
    map<short, short> mpmovierating = (*(mpCustomerMovieRating.find(CustID))).second;
    //cout<<(*mpmovierating.find(movieID)).second<<endl;
    if(mpmovierating.find(movieID) != mpmovierating.end())
        return (*(*(mpCustomerMovieRating.find(CustID))).second.find(movieID)).second; //.second[movieID];
    else
        return 0;
}
